#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<sys/types.h>
#include <stdlib.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>

#include <pthread.h>

#ifdef __APPLE__
#include <ifaddrs.h>
#endif


#include "data_unit.h"
#include "udpsend.h"
#include "cmd.h"
#include "data_integrity.h"

#include "media_device.h"
#include "pbc.h"

#define MAX_NETDEVICE_COUNT 10

typedef struct {
    dataunit_t du_head;

    char broadcast_ip[MAX_NETDEVICE_COUNT][INET_ADDRSTRLEN];

    char *devname[MAX_NETDEVICE_COUNT];
    char *devmac[MAX_NETDEVICE_COUNT];
    char devip[MAX_NETDEVICE_COUNT][INET_ADDRSTRLEN];

    int devcount;
    
} broadme_t;

static broadme_t gs_broadme;

int get_all_netdevice()
{
#ifdef __APPLE__
    struct ifaddrs        *ifc, *ifc1;
//    char                ip[INET_ADDRSTRLEN];
//    char                nm[INET_ADDRSTRLEN];
    int i = 0;
    
    if (0 != getifaddrs(&ifc)) return(-1);
    ifc1 = ifc;
    
    //printf("Iface\tIP address\tNetmask\n");
    for(; NULL != ifc; ifc = (*ifc).ifa_next) {
        //printf("%s", (*ifc).ifa_name);
        
        if((*ifc).ifa_name[0] == 'l') {
            continue;
        }
        
        if(gs_broadme.devname[i] != NULL) {
            free(gs_broadme.devname[i]);
            gs_broadme.devname[i] = NULL;
        }
        gs_broadme.devname[i] = strdup((*ifc).ifa_name);
   
        if (NULL != (*ifc).ifa_addr) {
            gs_broadme.devip[i][0] = 0;
            
            inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_addr))->sin_addr), gs_broadme.devip[i], INET_ADDRSTRLEN);
            
            printf("%s: %s", (*ifc).ifa_name, gs_broadme.devip[i]);
            
            if(strcmp(gs_broadme.devip[i], "0.0.0.0") == 0) {
                gs_broadme.devip[i][0] = 0;
                printf(" drop it\n");
                continue;
            }else {
                printf("\n");
            }
        } else {
            continue;
       //     printf("\t\t");
        }
        
        if (NULL != (*ifc).ifa_broadaddr) {
            gs_broadme.broadcast_ip[i][0] = 0;
            
            inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_broadaddr))->sin_addr), gs_broadme.broadcast_ip[i], INET_ADDRSTRLEN);

            printf("%s: %s", (*ifc).ifa_name, gs_broadme.broadcast_ip[i]);

            if(strcmp(gs_broadme.broadcast_ip[i], "0.0.0.0") == 0) {
                gs_broadme.broadcast_ip[i][0] = 0;
                printf(" drop it\n");
                continue;
            }else {
                printf("\n");
            }
        } else {
            continue;
           // printf("\t\t");
        }
        if (NULL != (*ifc).ifa_netmask) {
            //inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_netmask))->sin_addr), nm, 64);
            //printf("\t%s", nm);
        } else {
            //printf("\t\t");
        }
        //printf("\n");
        i++;
    }
    
    freeifaddrs(ifc1);
    
    gs_broadme.devcount = i;
    
    return(0);
    
#else
    struct ifreq ifr;
    struct ifconf ifc;
    char buf[2048];
    int i=0;

    
    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (sock == -1) {
        printf("socket error\n");
        return -1;
    }

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
        printf("ioctl error\n");
        return -1;
    }

    struct ifreq* it = ifc.ifc_req;
    const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
    char szMac[64];
    int count = 0;

    for (; it != end; ++it) {
        strcpy(ifr.ifr_name, it->ifr_name);
        printf("get ifreq.name=%s\n", ifr.ifr_name);
        if(strlen(ifr.ifr_name) == 0) {
            continue;
        }
        
        if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
            if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback

                if(ioctl(sock, SIOCGIFADDR, &ifr) == 0) {
                    //int ipval = ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr;
                    //inet_ntop(AF_INET, (void*)&ipval, gs_broadme.devip[i], INET_ADDRSTRLEN);

                    char *ipget = inet_ntoa( ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr) ;
                    gs_broadme.devip[i][0] = 0;
                    strcpy(gs_broadme.devip[i], ipget);
//                    printf("ipip %02X:%02X:%02X:%02X:%02X:%02X\n",*ptr,*(ptr+1),*(ptr+2),*(ptr+3),*(ptr+4),*(ptr+5));
                }

                if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
                    count ++ ;
                    unsigned char * ptr ;
                    
                    ptr = (unsigned char  *)&ifr.ifr_ifru.ifru_hwaddr.sa_data[0];
                    snprintf(szMac,64,"%02X:%02X:%02X:%02X:%02X:%02X",*ptr,*(ptr+1),*(ptr+2),*(ptr+3),*(ptr+4),*(ptr+5));
                    if(gs_broadme.devmac[i] != NULL) {
                        free(gs_broadme.devmac[i]);
                        gs_broadme.devmac[i] = NULL;
                    }
                    gs_broadme.devmac[i] = strdup(szMac);

                    if(gs_broadme.devname[i] != NULL) {
                        free(gs_broadme.devname[i]);
                        gs_broadme.devname[i] = NULL;
                    }
                    gs_broadme.devname[i] = strdup(ifr.ifr_name);
                    
                    printf("%d,Interface name : %s , Mac address : %s \n",count,ifr.ifr_name,szMac);
                }

                i++;
                
            }
        }else{
            printf("get mac info error\n");
            return -1;
        }
    }

    gs_broadme.devcount = i;
    return 0;
#endif
}

int __server_reponse(void *handler, unsigned char *buf, unsigned int buf_len, void *param){
    printf("%s\n", buf);

    return 0;
}

int _get_wifi_broadcast_ip(const char *dev_name, char *ipstr) {
    struct ifreq ifr;
    struct sockaddr_in addr;
    int sockfd;
    char ip[INET_ADDRSTRLEN];
    
    if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
        perror("socket");
        return -1;
    }
    
    strncpy(ifr.ifr_name, dev_name, sizeof(ifr.ifr_name));
    if (ioctl(sockfd, SIOCGIFBRDADDR, &ifr) == -1) {
        perror("ioctl");
        return -1;
    }
    
    memcpy(&addr, &ifr.ifr_broadaddr, sizeof(addr));
    if (inet_ntop(AF_INET, &addr.sin_addr, ip, sizeof(ip)) == NULL) {
        perror("inet_ntop");
        return -1;
    }
    
    if(ipstr != NULL) {
        ipstr[0] = 0;
        strncpy(ipstr, ip, INET_ADDRSTRLEN);
    }
    
    printf("broadcast address: %s\r\n", ip);

    if(sockfd>0)
        close(sockfd);
    
    return 0;
}

static struct pbc_env* __gen_pbc_env() {
    struct pbc_slice slice;

    slice.buffer = media_device;
    slice.len = media_device_len;

	struct pbc_env * env = pbc_new();
	int r = pbc_register(env, &slice);
	if (r) {
		printf("Error : %s", pbc_error(env));
		return NULL;
	}
    
    return env;
}

#if 0
static int _unpack_recv_data(struct pbc_env *env, unsigned char *buf, unsigned int buf_len, void *param) {
    struct pbc_slice slice;
    
    slice.buffer = buf;
    slice.len = buf_len;
    
    struct pbc_rmessage * m = pbc_rmessage_new(env, "MediaDevice.MediaDeviceMessage", &slice);
    if (m==NULL) {
        printf("Error : %s",pbc_error(env));
        return -1;
    }
    
    const char *cmd = pbc_rmessage_string(m , "cmd" , 0 , NULL);
    if(cmd!=NULL && strcmp(cmd, "CMD_DEVICE_BROADCAST") == 0) {
        struct pbc_rmessage * p = pbc_rmessage_message(m , "ip", 0);
        const char *ipstr = pbc_rmessage_string(p , "data", 0 ,NULL);
        
        printf("get a mediadevice ip=%s\n", ipstr);
    }else {
        printf("cmd == NULL\n");
    }
    
    
    pbc_rmessage_delete(m);
    
    return 0;
}
#endif

int __gen_ip_data(struct pbc_env *env, const char *ipstr, unsigned char *pack_buf, int *pack_len) {
    
	struct pbc_wmessage * msg = pbc_wmessage_new(env, "MediaDevice.MediaDeviceMessage");

	pbc_wmessage_string(msg , "cmd" , "CMD_DEVICE_BROADCAST" , -1);
    
	struct pbc_wmessage * ip = pbc_wmessage_message(msg , "ip");
	pbc_wmessage_string(ip, "data", ipstr, -1);

    struct pbc_slice slice;
    pbc_wmessage_buffer(msg, &slice);


    int cpy_len = *pack_len>slice.len?slice.len:*pack_len;

    memset(pack_buf, 0, *pack_len);
    memcpy(pack_buf, slice.buffer, cpy_len);
    *pack_len = cpy_len;
	
	pbc_wmessage_delete(msg);

    return 0;
}
int main(int argc, char **argv) {

    int i = 0;
    pcmd_t pcmd = NULL;
    pudpsend_t pudpsend = NULL;
    pdi_t pdi = NULL;
    dataunit_t du_response = {0};
    struct pbc_env* env = __gen_pbc_env();
    unsigned char sendbuf[100];

    du_next_unit_init(&gs_broadme.du_head);

    get_all_netdevice();


    du_next_unit_init(&du_response);
    du_response.recv = __server_reponse;
    
    cmd_alloc(&pcmd);
    udpsend_alloc(&pudpsend, (unsigned char*)"127.0.0.1", 8855);
    di_alloc(&pdi, 200);
    
    pcmd->init(pcmd);
    pudpsend->init(pudpsend);

    du_chain_t send_chain[] = {        
        {(pdataunit_t)pcmd, DATAUNIT_DIRECT_NONE, DATAUNIT_DIRECT_NONE},
        {(pdataunit_t)pdi, DATAUNIT_DIRECT_NONE, DATAUNIT_DIRECT_SEND},
        {(pdataunit_t)pudpsend, DATAUNIT_DIRECT_NONE, DATAUNIT_DIRECT_SEND},
    };

   
    du_chain_init(send_chain, sizeof(send_chain)/sizeof(send_chain[0]));

    int so_broadcast = 1;
    setsockopt(pudpsend->fd,SOL_SOCKET,SO_BROADCAST,&so_broadcast,sizeof(so_broadcast));

    while(1) {
        
        for(i=0; i<gs_broadme.devcount; i++) {
            _get_wifi_broadcast_ip(gs_broadme.devname[i], gs_broadme.broadcast_ip[i]);

            if(pudpsend->ipstr) {
                free(pudpsend->ipstr);
                pudpsend->ipstr = NULL;

                pudpsend->ipstr = (unsigned char*)strdup(gs_broadme.broadcast_ip[i]);
            }

            int packlen = sizeof(sendbuf);
            __gen_ip_data(env, gs_broadme.devip[i], sendbuf, &packlen);
            
#if 0
            int k=0;
            for(k=0; k<packlen; k++) {
                printf("%02X ", sendbuf[k]);
            }
            printf("\n");
            _unpack_recv_data(env, sendbuf, packlen, NULL);
#endif
            pdataunit_t writecmd = (pdataunit_t)pcmd;
            writecmd->send(writecmd, (unsigned char*)sendbuf, packlen, NULL);
        }

        sleep(10);
    }
    
    pcmd->deinit(pcmd);
    pudpsend->deinit(pudpsend);
    
    
    di_dealloc(&pdi);
    cmd_dealloc(&pcmd);
    udpsend_dealloc(&pudpsend);
    return 0;
    
}

